home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
linux-bo
/
etherboo.000
/
etherboo
/
etherboot-2.0
/
netboot-freebsd
/
ni52.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-01-12
|
16KB
|
485 lines
/*
* Netboot driver for ni5210. Ken Yap <ken@syd.dit.csiro.au>
*
* This driver is mostly adapted from Donald Becker's 3c507 driver for
* Linux since it was the clearest explanation of the 82586 chip I could
* find. It in turn was partly derived from the Crywnr packet driver.
* Parts of this driver were taken from Michael Hipp's ni5210 driver for
* Linux and the NCSA Telnet source provided more clues.
*
* The 82586 is a strange beast. It's best to think of it as a
* coprocessor. The ni5210 shares memory between the PC and the 82586.
* Commands are sent to the 82586 by depositing it in memory and then
* hitting the 82568 with an attention signal on an ioport. The fun comes
* from the interesting mapping of memory. In the ni5210 the window appears
* to be aligned with the bottom of 82586 memory with aliasing throughout
* the address space so that 1FFF or 3FFF (depending on 8k or 16k memory
* installed) is also FFFFFF in the 82586 address space for the purpose
* of bootup. Unfortunately there is no way to tell whether we have
* 8k or 16k of memory installed without actually doing an init since
* this jumper info is not readily available to the software. */
#include "netboot.h"
#ifdef NETBOOT32
#define fpeekw(w) (*((short *)(w)))
#endif
/* NI5210 IO addresses */
#define NI52_RESET 0 /* writing to this address, resets the *
* i82586 */
#define NI52_ATTENTION 1 /* channel attention, kick the 586 */
#define NI52_TDIS 2 /* Xmit disable */
#define NI52_TENA 3 /* Xmit enable */
#define NI52_INTENA 5 /* Interrupt enable */
#define NI52_INTDIS 4 /* Interrupt disable */
#define NI52_MAGIC1 6 /* dunno exact function */
#define NI52_MAGIC2 7 /* dunno exact function */
#define NI52_MAGICVAL1 0x00 /* magic-values for ni5210 card */
#define NI52_MAGICVAL2 0x55
#define NI52_ADDR0 0x02 /* ni5210 Ethernet addresses are 020701xxxxxx */
#define NI52_ADDR1 0x07
#define NI52_ADDR2 0x01
#define ni_reset586() outb(eth_nic_base+NI52_RESET,0)
#define ni_intdis() outb(eth_nic_base+NI52_INTDIS,0)
#define ni_tdis() outb(eth_nic_base+NI52_TDIS,0)
#define ni_tena() outb(eth_nic_base+NI52_TENA,0)
#define ni_attn586() outb(eth_nic_base+NI52_ATTENTION,0)
struct eth_header
{
unsigned char dst[ETHER_ADDR_SIZE];
unsigned char src[ETHER_ADDR_SIZE];
unsigned short type;
};
unsigned short eth_nic_base;
unsigned short eth_tx_link;
unsigned short eth_memsize;
Address eth_bmem;
Address eth_rmem;
unsigned char *eth_node_addr;
short aui;
/**************************************************************************
The following two variables are used externally
**************************************************************************/
char eth_driver[] = "ed0";
char packet[ETH_MAX_PACKET];
int packetlen;
/* The following came from 3c507.c, with some formatting cleanup. */
/*
* Details of the i82586.
*
* You'll really need the databook to understand the details of this part,
* but the outline is that the i82586 has two separate processing units.
* Both are started from a list of three configuration tables, of which
* only the last, the System Control Block (SCB), is used after reset-time.
* The SCB has the following fields: Status word Command word Tx/Command
* block addr. Rx block addr. The command word accepts the following
* controls for the Tx and Rx units: */
#define CUC_START 0x0100
#define CUC_RESUME 0x0200
#define CUC_SUSPEND 0x0300
#define RX_START 0x0010
#define RX_RESUME 0x0020
#define RX_SUSPEND 0x0030
/* The Rx unit uses a list of frame descriptors and a list of data buffer
* descriptors. We use full-sized (1518 byte) data buffers, so there is
* a one-to-one pairing of frame descriptors to buffer descriptors.
* The Tx ("command") unit executes a list of commands that look like:
* Status word Written by the 82586 when the command is done.
* Command word Command in lower 3 bits, post-command action in upper 3
* Link word The address of the next command.
* Parameters (as needed).
* Some definitions related to the Command Word are: */
#define CMD_EOL 0x8000 /* The last command of the list, stop. */
#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
enum commands
{
CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7
};
/* Offsets to registers in the mailbox (SCB). */
#define iSCB_STATUS 0x8
#define iSCB_CMD 0xA
#define iSCB_CBL 0xC /* Command BLock offset. */
#define iSCB_RFA 0xE /* Rx Frame Area offset. */
/*
* What follows in 'init_words[]' is the "program" that is downloaded to
* the 82586 memory. It's mostly tables and command blocks, and starts
* at the reset address 0xfffff6. This is designed to be similar to the
* EtherExpress, thus the unusual location of the SCB at 0x0008.
*
* Even with the additional "don't care" values, doing it this way takes
* less program space than initializing the individual tables, and I feel
* it's much cleaner.
*
* The databook is particularly useless for the first two structures, I
* had to use the Crynwr driver as an example.
*
* The memory setup is as follows: */
#define CONFIG_CMD 0x18
#define SET_SA_CMD 0x24
#define SA_OFFSET 0x2A
#define IDLELOOP 0x30
#define TDR_CMD 0x38
#define TDR_TIME 0x3C
#define DUMP_CMD 0x40
#define DIAG_CMD 0x48
#define SET_MC_CMD 0x4E
#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC
into. */
#define TX_BUF_START 0x0100
#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */
#define RX_BUF_START 0x0800
#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */
/*
* That's it: only 86 bytes to set up the beast, including every extra
* command available. The 170 byte buffer at DUMP_DATA is shared between
* the Dump command (called only by the diagnostic program) and the
* SetMulticastList command.
*
* To complete the memory setup you only have to write the station address
* at SA_OFFSET and create the Tx & Rx buffer lists.
*
* The Tx command chain and buffer list is setup as follows: A Tx command
* table, with the data buffer pointing to... A Tx data buffer descriptor.
* The packet is in a single buffer, rather than chaining together several
* smaller buffers. A NoOp command, which initially points to itself, And
* the packet data.
*
* A transmit is done by filling in the Tx command table and data buffer,
* re-writing the NoOp command, and finally changing the offset of the
* last command to point to the current Tx command. When the Tx command
* is finished, it jumps to the NoOp, when it loops until the next Tx
* command changes the "link offset" in the NoOp. This way the 82586
* never has to go through the slow restart sequence. */
unsigned short init_words[] =
{
/* System Configuration Pointer (SCP). */
0x0001, /* Set bus size to 8 bits. */
0, 0, /* pad words. */
0x0000, 0x0000, /* ISCP phys addr, set in
* init_82586_mem(). */
/* Intermediate System Configuration Pointer (ISCP). */
0x0001, /* Status word that's cleared when init is
* done. */
0x0008, 0, 0, /* SCB offset, (skip, skip) */
/* System Control Block (SCB). */
0, 0xF000 | CUC_START,/* SCB status and cmd. */
CONFIG_CMD, /* Command list pointer, points to
* Configure. */
RX_BUF_START, /* Rx block list. */
0, 0, 0, 0, /* Error count: CRC, align, buffer,
* overrun. */
/* 0x0018: Configure command. Change to put MAC data with packet.
*
*/
0, CmdConfigure, /* Status, command. */
SET_SA_CMD, /* Next command is Set Station Addr. */
0x0804, /* "4" bytes of config data, 8 byte FIFO. */
0x2e40, /* Magic values, including MAC data
* location. */
0, /* Unused pad word. */
/* 0x0024: Setup station address command. */
0, CmdSASetup,
SET_MC_CMD, /* Next command. */
0xaa00, 0xb000, 0x0bad, /* Station address (to be filled in) */
/* 0x0030: NOP, looping back to itself. Point to first Tx buffer
* to Tx. */
0, CmdNOp, IDLELOOP, 0 /* pad */ ,
/* 0x0038: A unused Time-Domain Reflectometer command. */
0, CmdTDR, IDLELOOP, 0,
/* 0x0040: An unused Dump State command. */
0, CmdDump, IDLELOOP, DUMP_DATA,
/* 0x0048: An unused Diagnose command. */
0, CmdDiagnose, IDLELOOP,
/* 0x004E: An empty set-multicast-list command. */
0, CmdMulticastList, IDLELOOP, 0,
};
/* End of stuff from 3c507.c */
int mem_probe P((void));
int i82586_probe P((void));
/**************************************************************************
ETH_PROBE - Look for an adapter
**************************************************************************/
int eth_probe()
{
int i;
char *name;
unsigned short chksum;
unsigned char c;
unsigned short *port;
static unsigned short ports[] =
{0x300, 0x280, 0x360, 0x320, 0x340, 0};
for (port = ports; *port != 0; port++)
{
unsigned short ioaddr = *port;
if (!(inb(ioaddr + NI52_MAGIC1) == NI52_MAGICVAL1) ||
!(inb(ioaddr + NI52_MAGIC2) == NI52_MAGICVAL2))
continue;
eth_nic_base = ioaddr;
eth_node_addr = arptable[ARP_CLIENT].node;
for (i = 0; i < 6; i++)
eth_node_addr[i] = inb(eth_nic_base + i);
if (eth_node_addr[0] != NI52_ADDR0
|| eth_node_addr[1] != NI52_ADDR1
|| eth_node_addr[2] != NI52_ADDR2)
continue;
printf("\r\nNI5210 base 0x%x, ", eth_nic_base);
/* we found the IObase, now try to find membase */
if (mem_probe())
break;
}
printf("memory 0x%X size 0x%x addr ", eth_bmem, eth_memsize);
for (i = 0; i < 6; i++)
{
printf("%b", (int)eth_node_addr[i]);
if (i < 5)
printf(":");
}
printf("\r\n");
eth_reset();
return (eth_nic_base != 0 && eth_bmem != 0);
}
int mem_probe()
{
static Address memaddrs[] =
{0xdc000, 0xc0000, 0xc4000, 0xc8000, 0xcc000, 0xd0000, 0xd4000,
0xd8000, 0xdc000, 0};
int i;
for (i = 0; (eth_bmem = memaddrs[i]) != 0; i++)
{
/* 8K-check */
bzero(eth_bmem, eth_memsize = 0x2000);
if (fbsame(eth_bmem, 0, eth_memsize) && i82586_probe())
break;
/* 16K-check */
bzero(eth_bmem, eth_memsize = 0x4000);
if (fbsame(eth_bmem, 0, eth_memsize) && i82586_probe())
break;
}
return (eth_bmem != 0, 1);
}
int i82586_probe()
{
static ushort probe_words[] =
{ 1, 8, 0, 0, /* ISCP */
0, 0, 0, 0, 0, 0, 0, 0 }; /* SCB */
ni_intdis();
ni_tdis();
ni_reset586();
DELAY(2000);
#ifdef NETBOOT32
/* Write the SCP words at 0xfff6 (address-aliased to 0xfffff6). */
bcopy(init_words, (char *)(eth_bmem + eth_memsize - 10), 10);
/* Write the ISCP, SCB and configure command words at 0x0000. */
bcopy(probe_words, (char *)eth_bmem, sizeof(probe_words));
#endif
#ifdef NETBOOT16
/* Write the SCP words at 0xfff6 (address-aliased to 0xfffff6). */
bcopyf(init_words, eth_bmem + eth_memsize - 10, 10);
/* Write the ISCP, SCB and configure command words at 0x0000. */
bcopyf(probe_words, eth_bmem, sizeof(probe_words));
#endif
ni_reset586();
ni_attn586();
DELAY(2000);
return (fpeekw(eth_bmem) == 0);
}
/**************************************************************************
ETH_RESET - Reset adapter
**************************************************************************/
int eth_reset()
{
ni_tdis();
ni_reset586();
DELAY(2000);
#ifdef NETBOOT32
/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
bcopy(init_words, (char *)(eth_bmem + eth_memsize - 10), 10);
/* Write the words at 0x0000. */
bcopy(init_words + 5, (char *)eth_bmem, sizeof(init_words) - 10);
/* Fill in the station address. */
bcopy(eth_node_addr, (char *)(eth_bmem + SA_OFFSET), ETHER_ADDR_SIZE);
#endif
#ifdef NETBOOT16
/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
bcopyf(init_words, eth_bmem + eth_memsize - 10, 10);
/* Write the words at 0x0000. */
bcopyf(init_words + 5, eth_bmem, sizeof(init_words) - 10);
/* Fill in the station address. */
bcopyf(eth_node_addr, eth_bmem + SA_OFFSET, ETHER_ADDR_SIZE);
#endif
eth_tx_link = IDLELOOP + 4; /* location of NOP block tx address */
ni_reset586();
ni_attn586();
DELAY(2000);
/* This was time consuming to track down: you need to give two
* channel attention signals to reliably start up the i82586. */
if (fpeekw(eth_bmem + iSCB_STATUS) == 0)
{
printf("i82586 initialization timed out with status %x, cmd %x\r\n",
fpeekw(eth_bmem + iSCB_STATUS),
fpeekw(eth_bmem + iSCB_CMD));
/* Issue channel-attn again */
ni_attn586();
DELAY(2000);
}
ni_tena();
printf("iSCB_STATUS = %x\r\n", fpeekw(eth_bmem + iSCB_STATUS));
return (fpeekw(eth_bmem + iSCB_STATUS) != 0);
}
/**************************************************************************
ETH_TRANSMIT - Transmit a frame
**************************************************************************/
#ifdef __STDC__
eth_transmit(char *d, unsigned short t, unsigned short s, char *p)
#else
eth_transmit(d, t, s, p)
char *d; /* Destination */
unsigned short t; /* Type */
unsigned short s; /* size */
char *p; /* Packet */
#endif
{
ushort a;
struct eth_header e;
static ushort buffer[11] = {
0x0000, /* Tx status */
CmdTx, /* Tx command */
TX_BUF_START + 16, /* Next command is a NoOp. */
TX_BUF_START + 8, /* Data Buffer offset. */
/* Data buffer descriptor. */
0x8000, /* Byte count parameter. */
-1, /* No next data buffer. */
TX_BUF_START + 22 + 0, /* Buffer follows
* the NoOp *
* command. */
0x0000, /* Buffer address high bits (always zero).
*
*/
/* Loop-back NoOp command. */
0x0000, /* Tx status */
CmdNOp, /* Tx command */
TX_BUF_START + 16 /* Next is myself. */
};
printf("Send %x bytes type %x from %X\r\n", s, t, p);
/* assemble header first */
bcopy(d, e.dst, ETHER_ADDR_SIZE);
bcopy(eth_node_addr, e.src, ETHER_ADDR_SIZE);
e.type = htons(t);
/* copy header then data into shared memory */
#ifdef NETBOOT32
bcopy(&e, (char *)(eth_bmem + TX_BUF_START + 22), sizeof(e));
bcopy(p, (char *)(eth_bmem + TX_BUF_START + 22 + sizeof(e)), s);
#endif
#ifdef NETBOOT16
bcopyf(&e, eth_bmem + TX_BUF_START + 22, sizeof(e));
bcopyf(p, eth_bmem + TX_BUF_START + 22 + sizeof(e), s); /* data */
#endif
#ifdef NETBOOT32
{
char b[100], *bp;
int i;
bp = b;
bcopy(eth_bmem + TX_BUF_START + 22, b, sizeof(b));
for (i = 0; i < sizeof(b); ++i)
{
printf(i % 20 ? " " : "\r\n");
printf("%b", *bp++);
}
printf("\r\n");
}
#endif
s += sizeof(e); /* for ether header */
if (s < ETH_MIN_PACKET)
s = ETH_MIN_PACKET;
buffer[4] = s | 0x8000;
a = TX_BUF_START;
printf("Modifying word at offset %d\r\n", eth_tx_link);
#ifdef NETBOOT32
bcopy(buffer, (char *)(eth_bmem + TX_BUF_START), sizeof(buffer));
/*bcopy(&a, (char *)(eth_bmem + eth_tx_link), sizeof(a));*/
#endif
#ifdef NETBOOT16
bcopyf(buffer, eth_bmem + TX_BUF_START, sizeof(buffer));
bcopyf(&a, eth_bmem + eth_tx_link, sizeof(a));
#endif
eth_tx_link = TX_BUF_START + 20; /* NOP block address */
printf("SCB status = %x\r\n", fpeekw(eth_bmem + iSCB_STATUS));
DELAY(2000);
printf("Transmit status = %x\r\n", fpeekw(eth_bmem + TX_BUF_START));
return (0);
}
/**************************************************************************
ETH_POLL - Wait for a frame
**************************************************************************/
eth_poll()
{
}
/* a surrogate */
#ifdef __STDC__
DELAY(int val)
#else
DELAY(val)
int val;
#endif
{
int c;
for (c = val; c > 0; --c)
twiddle();
}
#ifdef NETBOOT32
int fbsame(char *p, int c, int n)
{
while (n-- > 0)
if (*p++ != c)
return (0);
return (1);
}
#endif